package com.jxdinfo.hussar.activitidemo.utils.log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

import com.jxdinfo.hussar.activitidemo.utils.log.thread.LogWritePool;
import com.jxdinfo.hussar.activitidemo.utils.log.thread.LogWriteThread;
  
@Intercepts({  
        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),  
        @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,  
                RowBounds.class, ResultHandler.class }) })  
public class SqlInterceptor implements Interceptor {  
    /**
     * 注解拦截接口的方法
     * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
     * ParameterHandler (getParameterObject, setParameters)
     * ResultSetHandler (handleResultSets, handleOutputParameters)
     * StatementHandler (prepare, parameterize, batch, update, query)
     */

	private static final LogWritePool logWritePool=LogWritePool.getInstance();
    private Properties properties;  
   
    public Object intercept(Invocation invocation) throws Throwable {  
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];  
        Object parameter = null;  
       
        if (invocation.getArgs().length > 1) {  
            parameter = invocation.getArgs()[1];  
        }  
        String sqlId = mappedStatement.getId(); 
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);  
        Configuration configuration = mappedStatement.getConfiguration();  
	    try{
	        Object returnValue = null;  
	        long start = System.currentTimeMillis();  
	        returnValue = invocation.proceed();  
	        long end = System.currentTimeMillis();  
	        long time = (end - start);  
	        if (time > 1) { 
	        	String lCount = "0";
	        	String log ="";
	        	if ( returnValue instanceof List){
	        		lCount = String.valueOf(((List)returnValue).size());
	        		log = getSql(configuration, boundSql, sqlId, time,lCount);
	        	}
	        	else{
	        		log = getSql(configuration, boundSql, sqlId, time,returnValue);
	            }
	        	
	            logWritePool.execute(new LogWriteThread(log,"info"));  
	        }  
	        return returnValue;
         }
         catch(Exception e){
        	 try {
                 // 防止死循环
                 if (mappedStatement.getId().toUpperCase()
                         .contains("sqlExceptionLogMapper".toUpperCase())) {
                     throw e;
                 }
                 String sql = showSql(configuration, boundSql);  
                 StringBuilder str = new StringBuilder(100);  
                 str.append(sqlId);  
                 str.append(":");  
                 str.append(sql);  
                 //str.append(":");  
                 //str.append(parameter.toString());  
                 
                 str.append(":");
                 str.append(e.getCause().toString());
                 str.append(":");
                // str.append(getExceptionStackTrace(e));
                 logWritePool.execute(new LogWriteThread(str.toString(),"error")); 
             } catch (Exception ex) {
                 ex.printStackTrace();
             }
             // 抛出异常
            // throw e;
        	 return null;
         }
    }  
   
    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId, long time,Object changeCount) {  
        String sql = showSql(configuration, boundSql);  
        StringBuilder str = new StringBuilder(100);  
        str.append(sqlId);  
        str.append(":");  
        str.append(sql);  
        str.append(":");  
        str.append(time);     
        str.append("ms"); 
        str.append(":");  
        str.append(changeCount.toString());    
        return str.toString();  
    }  
   
    private static String getParameterValue(Object obj) {  
        String value = null;  
        if (obj instanceof String) {  
            value = "'" + obj.toString() + "'";  
        } else if (obj instanceof Date) {  
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);  
            value = "'" + formatter.format(new Date()) + "'";  
        } else {  
            if (obj != null) {  
                value = obj.toString();  
            } else {  
                value = "";  
            }  
   
        }  
        return value;  
    }  
   
    public static String showSql(Configuration configuration, BoundSql boundSql) {  
        Object parameterObject = boundSql.getParameterObject();  
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");  
        if (parameterMappings.size() > 0 && parameterObject != null) {  
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();  
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {  
                sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
            } else {  
                MetaObject metaObject = configuration.newMetaObject(parameterObject);  
                for (ParameterMapping parameterMapping : parameterMappings) {  
                    String propertyName = parameterMapping.getProperty();  
                    if (metaObject.hasGetter(propertyName)) {  
                        Object obj = metaObject.getValue(propertyName);  
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));  
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {  
                        Object obj = boundSql.getAdditionalParameter(propertyName);  
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));  
                    } else{  
                    	
                        if(parameterObject instanceof Map){
                           	Map map = (Map)parameterObject ;  
                        	 sql = sql.replaceFirst("\\?", getParameterValue(map.get(propertyName)));  
                        }
                        else if(parameterObject instanceof String){
                           	
                        	 sql = sql.replaceFirst("\\?", String.valueOf(parameterObject));  
                        }
                        else{
                        	
                        }
                    }    
                }  
            }  
        }  
        return sql;  
    }  
   
    public Object plugin(Object target) {  
        return Plugin.wrap(target, this);  
    }  
   
    public void setProperties(Properties properties0) {  
        this.properties = properties0;  
    }  
    /**
     * 获取异常的堆栈信息.
     * 
     * @param e
     * @return
     */
    private String getExceptionStackTrace(Exception e) {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        e.printStackTrace(new java.io.PrintWriter(buf, true));
        String expMessage = buf.toString();
        try {
            buf.close();
        } catch (IOException ex) {
        }
        return expMessage;
    }
}  
